﻿namespace WPForms.Phone.View
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using Microsoft.Live;
    using PhoneUtils.View;
    using WPForms.Phone.Model;

    /// <summary>
    /// View (and logic) responsible for handling skydrive backups.
    /// </summary>
    /// <remarks>
    /// Databinding the SignInButton is not supported by default, so MVVM is not implemented in this case.
    /// Might be imporoved by finding a workaround.
    /// </remarks>
    public partial class SkyDriveBackup : NavigationAwarePage
    {
        /// <summary>
        /// The current live session.
        /// </summary>
        private LiveConnectSession session;

        /// <summary>
        /// The root entry
        /// </summary>
        private EntryResult currentEntry = new EntryResult() { Id = "/me/skydrive" };

        /// <summary>
        /// The path of the database file.
        /// </summary>
        private string databaseFile;

        /// <summary>
        /// Initializes a new instance of the <see cref="SkyDriveBackup"/> class.
        /// </summary>
        public SkyDriveBackup()
        {
            var connectionString = PhoneUtils.Services.ContainerLocator.Current.Resolve<Bootstrapper>().ConnectionString;
            this.databaseFile = connectionString.Split(new string[] { "isostore:/" }, StringSplitOptions.RemoveEmptyEntries)[0];
            this.InitializeComponent();
            VisualStateManager.GoToState(this, VisualStates.SignedOut.ToString(), false);
            VisualStateManager.GoToState(this, VisualStates.ContentLoaded.ToString(), true);
        }

        /// <summary>
        /// Enum for the visual states.
        /// </summary>
        private enum VisualStates
        {
            /// <summary>
            /// No visual state set.
            /// </summary>
            None,

            /// <summary>
            /// User is signed out
            /// </summary>
            SignedOut,

            /// <summary>
            /// User is signed in
            /// </summary>
            SignedIn,

            /// <summary>
            /// Currently loading
            /// </summary>
            Loading,

            /// <summary>
            /// Content loaded.
            /// </summary>
            ContentLoaded
        }

        /// <summary>
        /// Called just before a page is no longer the active page in a frame.
        /// </summary>
        /// <param name="e">An object that contains the event data.</param>
        protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
        {
            base.OnNavigatingFrom(e);
            if (this.currentEntry.Parent != null && e.IsCancelable)
            {
                this.currentEntry = this.currentEntry.Parent;
                this.ApplyCurrent();
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Handles the SessionChanged event of the SignIn control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs" /> instance containing the event data.</param>
        private void SignIn_SessionChanged(object sender, Microsoft.Live.Controls.LiveConnectSessionChangedEventArgs e)
        {
            if (e.Status == LiveConnectSessionStatus.Connected)
            {
                VisualStateManager.GoToState(this, VisualStates.SignedIn.ToString(), true);
                this.session = e.Session;
                LiveConnectClient client = new LiveConnectClient(this.session);
                client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(this.GetFolderFiles_Completed);
                client.GetAsync("/me/skydrive/files");
                VisualStateManager.GoToState(this, VisualStates.Loading.ToString(), true);
            }
        }

        /// <summary>
        /// Handles the Completed event of the SignIn control, when the folder file list is downloaded.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="LiveOperationCompletedEventArgs" /> instance containing the event data.</param>
        private void GetFolderFiles_Completed(object sender, LiveOperationCompletedEventArgs e)
        {
            VisualStateManager.GoToState(this, VisualStates.ContentLoaded.ToString(), true);
            if (e.Error == null)
            {
                var folders = new List<EntryResult>();
                var records = (List<object>)e.Result["data"];
                foreach (Dictionary<string, object> record in records)
                {
                    var newEntry = new EntryResult()
                        {
                            Name = record["name"].ToString(),
                            Id = record["id"].ToString(),
                            Parent = this.currentEntry,
                            IsDirectory = record["type"].ToString() == "folder",
                            IsFile = record["type"].ToString() == "file",
                        };
                    newEntry.IsEnabled = newEntry.IsDirectory || record["type"].ToString() == "album" || newEntry.Name.EndsWith(".sdf");

                    folders.Add(newEntry);
                }

                this.currentEntry.Children = folders.OrderBy(o => o.IsFile).ThenBy(o => o.Name).ToList();
                this.ApplyCurrent();
            }
        }

        /// <summary>
        /// Handles the DownloadCompleted event of the client control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.Live.LiveDownloadCompletedEventArgs"/> instance containing the event data.</param>
        private void DownloadCompleted(object sender, LiveDownloadCompletedEventArgs e)
        {
            VisualStateManager.GoToState(this, VisualStates.ContentLoaded.ToString(), true);
            if (e.Error == null)
            {
                var storage = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
                using (var writeStream = storage.OpenFile(this.databaseFile, System.IO.FileMode.Create, System.IO.FileAccess.Write))
                {
                    using (var reader = new System.IO.BinaryReader(e.Result))
                    {
                        int length = (int)e.Result.Length;
                        writeStream.Write(reader.ReadBytes(length), 0, length);
                    }
                }

                MessageBox.Show("Backup loaded successfuly!");
            }
            else
            {
                MessageBox.Show("Download failed! Please try again later...");
            }
        }

        /// <summary>
        /// Updates all controls on the screen to match the current entry.
        /// </summary>
        private void ApplyCurrent()
        {
            this.currentPath.Text = "browsing: " + this.GetCurrentPath();
            this.selectButton.Visibility = System.Windows.Visibility.Visible;
            this.selectButton.IsEnabled = true;
            this.folders.ItemsSource = this.currentEntry.Children;
            this.upButton.Visibility = this.currentEntry.Parent == null ? System.Windows.Visibility.Collapsed : System.Windows.Visibility.Visible;
        }

        /// <summary>
        /// Gets the path of the given entry on the skydrive.
        /// </summary>
        /// <param name="entry">The entry. The default value will be the current entry.</param>
        /// <returns>The path for the given entry.</returns>
        private string GetCurrentPath(EntryResult entry = null)
        {
            entry = entry ?? this.currentEntry;
            if (entry.Parent == null)
            {
                return "skydrive";
            }

            return this.GetCurrentPath(entry.Parent) + "/" + entry.Name;
        }

        /// <summary>
        /// Handles the Click event of the Up control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Up_Click(object sender, RoutedEventArgs e)
        {
            this.currentEntry = this.currentEntry.Parent;
            this.ApplyCurrent();
        }

        /// <summary>
        /// Handles the Click event of the SelectButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void SelectButton_Click(object sender, RoutedEventArgs e)
        {
            var client = new LiveConnectClient(this.session);
            var storage = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
            var readStream = storage.OpenFile(this.databaseFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
            client.UploadCompleted += new EventHandler<LiveOperationCompletedEventArgs>(this.Client_UploadCompleted);
            client.UploadAsync(
                this.currentEntry.Id,
                this.filename.Text + ".sdf",
                readStream,
                OverwriteOption.Overwrite);
            VisualStateManager.GoToState(this, VisualStates.Loading.ToString(), true);
        }

        /// <summary>
        /// Handles the UploadCompleted event of the Client control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.Live.LiveOperationCompletedEventArgs"/> instance containing the event data.</param>
        private void Client_UploadCompleted(object sender, LiveOperationCompletedEventArgs e)
        {
            var readStream = e.UserState as System.IO.Stream;
            if (readStream != null)
            {
                readStream.Close();
            }

            VisualStateManager.GoToState(this, VisualStates.ContentLoaded.ToString(), true);
            if (e.Error == null)
            {
                this.currentEntry = new EntryResult() { Id = "/me/skydrive" };
                LiveConnectClient client = new LiveConnectClient(this.session);
                client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(this.GetFolderFiles_Completed);
                client.GetAsync("/me/skydrive/files");
                MessageBox.Show("Backup complete!");
            }
            else
            {
                MessageBox.Show("Backup failed!");
            }
        }

        /// <summary>
        /// Handles the ItemSelected event of the Folders control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="SelectionChangedEventArgs" /> instance containing the event data.</param>
        private void Folders_ItemSelected(object sender, SelectionChangedEventArgs e)
        {
            var selectedItem = this.folders.SelectedItem as EntryResult;
            if (selectedItem == null || !selectedItem.IsEnabled)
            {
                return;
            }

            if (selectedItem.Name.EndsWith(".sdf"))
            {
                // user selected a backup file, try to download and return
                var client = new LiveConnectClient(this.session);
                client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(this.DownloadCompleted);
                client.DownloadAsync(selectedItem.Id + "/content");
                VisualStateManager.GoToState(this, VisualStates.Loading.ToString(), true);
                return;
            }

            this.currentEntry = this.currentEntry.Children.Single(entry => entry.Id == selectedItem.Id);
            if (this.currentEntry.Children == null)
            {
                // the entry's children collection is not loaded, so start to download from skydrive
                var client = new LiveConnectClient(this.session);
                client.GetCompleted += new EventHandler<LiveOperationCompletedEventArgs>(this.GetFolderFiles_Completed);
                client.GetAsync(selectedItem.Id + "/files");
                VisualStateManager.GoToState(this, VisualStates.Loading.ToString(), true);
            }
            else
            {
                // the entry is loaded, so let's display
                this.ApplyCurrent();
            }
        }
    }
}
